package com.hui.stock.service.impl;

import com.alibaba.excel.EasyExcel;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.hui.stock.mapper.StockBusinessMapper;
import com.hui.stock.mapper.StockMarketIndexInfoMapper;
import com.hui.stock.mapper.StockOuterMarketIndexInfoMapper;
import com.hui.stock.mapper.StockRtInfoMapper;
import com.hui.stock.pojo.domain.*;
import com.hui.stock.pojo.entity.StockRtInfo;
import com.hui.stock.pojo.vo.StockInfoConfig;
import com.hui.stock.service.StockService;
import com.hui.stock.utils.DateTimeUtil;
import com.hui.stock.vo.resp.PageResult;
import com.hui.stock.vo.resp.R;
import com.hui.stock.vo.resp.ResponseCode;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.GetMapping;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author: ymh
 * @date: 2024/12/2 16:52
 * @description:
 */
@Service
@Slf4j
public class StockServiceImpl implements StockService {

    @Autowired
    private StockInfoConfig stockInfoConfig;
    @Autowired
    private StockMarketIndexInfoMapper stockMarketIndexInfoMapper;
    @Autowired
    private StockRtInfoMapper stockRtInfoMapper;
    @Autowired
    private StockOuterMarketIndexInfoMapper stockOuterMarketIndexInfoMapper;
    @Autowired
    private StockBusinessMapper stockBusinessMapper;
    /**
     * 注入本地缓存bean
     */
    @Autowired
    private Cache<String,Object> caffeineCache;

    @ApiOperation(value = "获取A股大盘最新有效时间数据")
    @Override
    public R<List<InnerMarketDomain>> getInnerMarkerInfo() {
        //TODO get:从缓存中加载数据，如果不存在，则走补偿策略获取数据，并存入本地缓存
        R<List<InnerMarketDomain>> data = (R<List<InnerMarketDomain>>) caffeineCache.get("innerMarketInfos", key -> {
            //1 获取当前最近的有效时间
            Date curDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
            curDate = DateTime.parse("2022-01-02 09:32:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
            //2 获取A股大盘股票编码
            List<String> mCodes = stockInfoConfig.getInner();
            //3 调用mapper查询数据
            List<InnerMarketDomain> infos = stockMarketIndexInfoMapper.getMarketInfo(curDate, mCodes);
            //4 封装并返回
            return R.ok(infos);
        });
        return data;
    }

    @ApiOperation(value = "获取板块最新有效时间的按照交易金额降序排序的前10条数据")
    @Override
    public R<List<StockBlockDomain>> getBlockInfo() {
        //1 获取当前最近的有效时间
        Date curDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
        curDate = DateTime.parse("2021-12-21 09:30:01", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //2 调用mapper返回数据
        List<StockBlockDomain> data = stockMarketIndexInfoMapper.getBlockInfo(curDate);
        if (CollectionUtils.isEmpty(data)){
            return R.error(ResponseCode.NO_RESPONSE_DATA.getMessage());
        }
        return R.ok(data);
    }

    /**
     * 分页查询股票最新数据，并按照涨幅排序查询
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public R<PageResult> getStockPageInfo(Integer page, Integer pageSize) {
        //1 获取当前时间最近的有效时间
        Date curDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
        //2 假数据
        curDate = DateTime.parse("2022-07-13 14:49:00",DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //3 使用PageHelper启动分页
        PageHelper.startPage(page,pageSize);
        //4 调用Mapper返回分页数据
        List<StockUpdownDomain> data = stockRtInfoMapper.getStockPageInfo(curDate);
        if (CollectionUtils.isEmpty(data)){
            return R.error(ResponseCode.NO_RESPONSE_DATA.getMessage());
        }
        //5 组装返回页面对象,PageInfo会从线程中自动获取PageHelper的查询结果（页面，总页数，当前页....）
        PageInfo<StockUpdownDomain> pageInfo = new PageInfo<>(data);
        PageResult<StockUpdownDomain> pageResult = new PageResult<>(pageInfo);
        return R.ok(pageResult);
    }

    /**
     * 查询股票最新的前四条按照涨幅降序排序的数据
     * @return
     */
    @Override
    public R<List<StockUpdownDomain>> getStockRtInfo() {
        //1 获取当前最近的有效时间
        Date curDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
        //TODO 假数据
        curDate = DateTime.parse("2022-07-13 14:49:00",DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //2 调用mapper层获取数据
        List<StockUpdownDomain> data = stockRtInfoMapper.getStockRtInfo(curDate);
        if (CollectionUtils.isEmpty(data)){
            return R.error(ResponseCode.NO_RESPONSE_DATA.getMessage());
        }
        return R.ok(data);
    }

    /**
     * 获取当前的有效交易日(开盘时间-->收盘/当前时间)每分钟达到涨跌停股票的数据
     * @return
     */
    @Override
    public R<Map> getStockUpdownCount() {
        //1 获取当前时间最近的有效股票交易时间
        DateTime curDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        //TODO 假数据
        curDateTime = DateTime.parse("2022-01-06 14:25:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date curDate = curDateTime.toDate();
        //2 获取开盘时间
        DateTime openDateTime = DateTimeUtil.getOpenDate(curDateTime);
        Date openDate = openDateTime.toDate();
        //3 调用mapper层获取涨停数据
        List<Map> upList = stockRtInfoMapper.getStockUpdownCount(openDate,curDate,1);
        //4 调用mapper层获取跌停数据
        List<Map> downList = stockRtInfoMapper.getStockUpdownCount(openDate,curDate,0);
        //5 封装数据
        Map<String,List> map = new HashMap<>();
        map.put("upList",upList);
        map.put("downList",downList);
        return R.ok(map);
    }

    /**
     * 将指定页的股票数据导出到excel表下
     * @param page
     * @param pageSize
     * @param response
     */
    @Override
    public void stockExport(Integer page, Integer pageSize, HttpServletResponse response) {
        //1 调用getStockPageInfo获取分页数据
        R<PageResult> stockPageInfo = this.getStockPageInfo(page, pageSize);
        List<StockUpdownDomain> rows = stockPageInfo.getData().getRows();
        response.setCharacterEncoding("utf-8");
        //2 集合为空
        if (CollectionUtils.isEmpty(rows)) {
            //响应提示信息
            sendErrorResponse(response, ResponseCode.NO_RESPONSE_DATA);
        }
        response.setContentType("application/vnd.ms-excel");
        try {
            String fileName = URLEncoder.encode("股票信息表", "UTF-8");
            response.setHeader("Content-disposition","attachment;filename="+fileName+".xlsx");
            EasyExcel.write(response.getOutputStream(),StockUpdownDomain.class).sheet("股票涨幅信息").doWrite(rows);
        } catch (IOException e) {
            log.error("当前页码:{},每页大小:{},当前时间:{},异常信息:{}",page,page,DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),e.getMessage());
            sendErrorResponse(response, ResponseCode.ERROR);
        }
    }

    /**
     * 统计沪深两市T日和T-1日每分钟的成交量
     * @return
     */
    @Override
    public R<Map<String, List>> getComparedStockTradeAmt() {
        //1 获取当前最近的有效交易时间
        DateTime curDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        //TODO 假数据
        curDateTime = DateTime.parse("2021-12-28 14:40:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date curDate = curDateTime.toDate();
        //2 获取有效交易时间当天的开盘时间
        DateTime openDateTime = DateTimeUtil.getOpenDate(curDateTime);
        Date openDate = openDateTime.toDate();
        //3 获取当前最近有效交易日区间的上一个交易日区间
        DateTime preDateTime = DateTimeUtil.getPreviousTradingDay(curDateTime);
        //TODO 假数据
        preDateTime = DateTime.parse("2021-12-27 14:40:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date preDate = preDateTime.toDate();
        DateTime preOpenDateTime = DateTimeUtil.getOpenDate(preDateTime);
        Date preOpenDate = preOpenDateTime.toDate();
        //4 调用mapper获取最近的有效交易日区间的信息
        List<Map> amtList = stockMarketIndexInfoMapper.getStockTradeAmt(curDate,openDate,stockInfoConfig.getInner());
        //5 调用mapper获取最近的有效交易日区间的前一个有效交易日区间的信息
        List<Map> yesAmtList = stockMarketIndexInfoMapper.getStockTradeAmt(preDate,preOpenDate,stockInfoConfig.getInner());
        //6 组装数据
        Map<String,List> data = new HashMap<>();
        data.put("amtList",amtList);
        data.put("yesAmtList",yesAmtList);
        return R.ok(data);
    }

    /**
     * 统计当前时间下（精确到分钟），A股在各个涨跌区间股票的数量
     * @return
     */
    @Override
    public R<Map> getStockUpDownScopeCount() {
        //1 获取当前最近的有效交易时间
        DateTime curDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        //TODO 假数据
        curDateTime = DateTime.parse("2021-12-28 14:40:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date curDate = curDateTime.toDate();
        //2 调用mapper层返回当前分钟A股在各个涨跌区间股票的数量
        List<Map> infos = stockMarketIndexInfoMapper.getStockUpDownScopeCount(curDate);
        if (CollectionUtils.isEmpty(infos)){
            return R.error(ResponseCode.NO_RESPONSE_DATA);
        }
        //2.1 获取有序的标题集合
        List<String> list = stockInfoConfig.getUpDownRange();
        List<Map> maps = list.stream().map(title -> {
            Map mp = null;
            Optional<Map> op = infos.stream().filter(m -> m.containsValue(title)).findFirst();
            if (op.isPresent()) {
                mp = op.get();
            } else {
                mp = new HashMap();
                mp.put("count", 0);
                mp.put("title", title);
            }
            return mp;
        }).collect(Collectors.toList());
        //3 封装数据
        String time = curDateTime.toString("yyyy-MM-dd HH:mm:ss");
        Map<String,Object> data = new HashMap<>();
        data.put("time",time);
        data.put("infos",maps);
        return R.ok(data);
    }

    /**
     * 查询个股的分时行情数据，统计指定股票T日每分钟的交易数据
     * @param code
     * @return
     */
    @Override
    public R<List<Stock4MinuteDomain>> getStockScreenTimeSharing(String code) {
        //1 获取当前最近的有效交易时间
        DateTime curDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        //TODO 假数据
        curDateTime = DateTime.parse("2021-12-28 14:40:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date curDate = curDateTime.toDate();
        //2 获取有效交易时间当天的开盘时间
        DateTime openDateTime = DateTimeUtil.getOpenDate(curDateTime);
        Date openDate = openDateTime.toDate();
        //3 调用mapper层返回数据
        List<Stock4MinuteDomain> data = stockRtInfoMapper.getStockScreenTimeSharing(openDate,curDate,code);
        if (CollectionUtils.isEmpty(data)){
            return R.error(ResponseCode.NO_RESPONSE_DATA);
        }
        return R.ok(data);
    }

    /**
     * 查询指定股票每天产生的数据，组装成日K线数据
     * @param code
     * @return
     */
    @Override
    public R<List<Stock4EvrDayDomain>> getStockScreenDkline(String code) {
        //1 获取截止时间
        DateTime curDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        //TODO 假数据
        curDateTime = DateTime.parse("2022-01-07 15:00:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date curDate = curDateTime.toDate();
        //2 开始时间
        DateTime openDateTime = curDateTime.minusDays(10);
        Date openDate = openDateTime.toDate();
        //3 调用mapper层获取数据
        //3.1 获取时间段内每天的最大时间
        List<Date> closeDates = stockRtInfoMapper.getCloseDates(code,openDate,curDate);
        //3.2 根据每天的收盘时间获取指定股票的数据
        List<Stock4EvrDayDomain> data = stockRtInfoMapper.getStockScreenDkline(code,closeDates);
        return R.ok(data);
    }
    /**
     * 查询指定股票每天产生的数据，组装成周K线数据
     * @param code
     * @return
     */
    @Override
    public R<List<Stock4EvrWeekDomain>> getStockScreenWeekkline(String code) {
        //1 获取截止时间
        DateTime curDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        //TODO 假数据
        curDateTime = DateTime.parse("2022-01-19 15:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date curDate = curDateTime.toDate();
        //2 开始时间
        DateTime openDateTime = DateTimeUtil.getOpenDate(curDateTime.minusMonths(1));
        Date openDate = openDateTime.toDate();
        //3 调用mapper层获取数据
        List<Stock4EvrWeekDomain> list = stockRtInfoMapper.getStockScreenWeekkline(code,openDate,curDate);
        return R.ok(list);
    }

    /**
     * 获取个股最新分时行情数据
     * @param code 股票编码
     * @return
     */
    @Override
    public R<StockRtDomain> getStockScreenSecondDetail(String code) {
        //1 获取最近的有效交易时间
        DateTime curDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        //TODO 假数据
        curDateTime = DateTime.parse("2021-12-19 14:44:00",DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date curDate = curDateTime.toDate();
        //2 调用mapper获取数据
        StockRtDomain stockRtDomain = stockRtInfoMapper.getStockScreenSecondDetail(code,curDate);
        return R.ok(stockRtDomain);
    }

    /**
     * 个股交易流水行情数据查询--查询最新交易流水，按照交易时间降序取前10
     * @param code 股票编码
     * @return
     */
    @Override
    public R<List<StockScreenSecondDomain>> getScreenSecondInfos(String code) {
        //1 获取最近的有效交易时间
        DateTime curDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        //TODO 假数据
        curDateTime = DateTime.parse("2021-12-19 14:44:00",DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date curDate = curDateTime.toDate();
        //2 开始时间
        DateTime openDateTime = DateTimeUtil.getOpenDate(curDateTime);
        Date openDate = openDateTime.toDate();
        //3 调用mapper获取数据
        List<StockScreenSecondDomain> list = stockRtInfoMapper.getScreenSecondInfos(code,openDate,curDate);
        return R.ok(list);
    }


    /**
     * 获取国外大盘最新有效时间数据
     * @return
     */
    @Override
    public R<List<OuterMarketDomain>> getOuterMarkerInfo() {
        //1 获取最近的有效交易时间
        Date curDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
        //TODO 假数据
        curDate = DateTime.parse("2021-12-01 10:57:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //2 获取外盘编码
        List<String> outerCodes = stockInfoConfig.getOuter();
        //3 调用mapper获取数据
        List<OuterMarketDomain> infos = stockOuterMarketIndexInfoMapper.getOuterMarkerInfo(curDate, outerCodes);
        if (CollectionUtils.isEmpty(infos)){
            return R.error(ResponseCode.NO_RESPONSE_DATA);
        }
        return R.ok(infos);
    }

    /**
     * 根据输入的个股代码，进行模糊查询，返回证券代码和证券名称
     * @param searchStr
     * @return
     */
    @Override
    public R<List<Map<String, Object>>> getStockSearch(String searchStr) {
        //1 参数检验
        if (Strings.isBlank(searchStr)){
            return R.error(ResponseCode.DATA_ERROR);
        }
        //2 调用mapper返回
        List<Map<String, Object>> list = stockRtInfoMapper.getStockSearch(searchStr);
        if (CollectionUtils.isEmpty(list)){
            return R.error(ResponseCode.NO_RESPONSE_DATA);
        }
        return R.ok(list);
    }

    /**
     * 个股主营业务查询接口:根据股票编码获取个股主营业务
     * @param code 股票编码
     * @return
     */
    @Override
    public R<StockBusinessDomain> getStockBusiness(String code) {
        //1 判空
        if (Strings.isBlank(code)){
            return R.error(ResponseCode.NO_RESPONSE_DATA);
        }
        //2 调用mapper返回数据
        StockBusinessDomain sbd = stockBusinessMapper.getStockBusiness(code);
        return R.ok(sbd);
    }



    // 提取公共响应错误信息方法
    private void sendErrorResponse(HttpServletResponse response, ResponseCode responseCode) {
        response.setContentType("application/json");
        try {
            R<Object> r = R.error(responseCode);
            response.getWriter().write(new ObjectMapper().writeValueAsString(r));
        } catch (IOException e) {
            log.info("stockExport:响应错误信息失败，时间:{}",
                    DateTime.now().toString("yyyy-MM-dd HH:mm:ss"));
        }
    }
}
